﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Devsoft.Utilities
{
    
    public class TaskManager<T>// where T:new ()
    {
        
        public TaskManager(Action<T> action)
        {
            Action = action;
            TryTimes = 3;
            ThrowExceptionAble = true;
            _queue = new Queue<T>();
        }

        #region Properties

        public int Total
        {
            get { return _total; }
        }

        public int Completed
        {
            get { return _completed; }
        }

        public int QueueCount
        {
            get { return _queue.Count; }
        }

        public int Threads { get; set; }
        public bool UseBackgroundThread { get; set; }
        public string ThreadNamePrefix { get; set; }
        public Action<T> Action { get; set; }
		public Action<Exception> ErrorHandler { get; set; }

	    public int TryTimes { get; set; }

        public bool ThrowExceptionAble { get; set; }

        #endregion
        /*
        public void StartEnqueue()
        {
            _enqueueing = true;
        }*/

        public void StopEnqueue()
        {
            _enqueueing = false;
        }

        public TaskManager<T> Start(bool startEnqueueing = true)
        {
            if (startEnqueueing)
            {
                _enqueueing = true;
            }
            _threads = new List<Thread>();
            var prefix = string.IsNullOrEmpty(ThreadNamePrefix) ? "TaskManager " : ThreadNamePrefix;
            for (int i = 0; i < Threads; i++)
            {
                var thread = new Thread(Run)
                    {
                        Name = string.Format("{0} #{1}", prefix, i + 1)
                    };
                _threads.Add(thread);
                thread.Start();
            }
            return this;
        }

        public void Run()
        {
            var item = GetNextItem();
            while (_enqueueing || item != null)
            {
                if (item == null)
                {
                    Thread.Sleep(1);
                }
                else if(Action != null)
                {
	                TaskHelper.TryWithErrorHandler(() => Action(item),
	                                               numTrys: TryTimes,
	                                               throwErrorAble: ThrowExceptionAble,
	                                               errorhandler: ErrorHandler);
                    _completed = Completed + 1;
                }
                item = GetNextItem();
            }
        }

        public void Enqueue(T item)
        {
            lock (_queue)
            {
                _queue.Enqueue(item);
                _total = Total + 1;    
            }
            
        }

        public void WaitForComplete(bool endEnqueueing = true)
        {
            Thread.Sleep(200);
            if (endEnqueueing)
                StopEnqueue();
            while (true)
            {
                lock (_queue)
                {
                    if (_queue.Count == 0)
                    {
                        break;
                    }
                }
            }
            do
            {
                Thread.Sleep(1000);
                if (_threads.All(t => t.IsAlive == false))
                {
                    return;
                }
            } while (true);
        }

        public Timer SimpleReportOnConsoleTitle(string prefix = "",int interval = 1000)
        {
#if MONODROID
            return null;
#else
            return new Timer((state) =>
            {
                var manager = this;
                Console.Title = string.Format("{0} Total: {1} Queue: {2} Completed: {3}", prefix, manager.Total, manager.QueueCount, manager.Completed);
            }, this, 1000, 1000);
#endif
            
        }

        protected T GetNextItem()
        {
            lock (_queue)
            {
                if (_queue.Count > 0)
                {
                    return _queue.Dequeue();
                }
                return default(T);

            }
        }

        
        
        
        

        private Queue<T> _queue;
        private bool _enqueueing;
        private List<Thread> _threads;
        private int _total;
        private int _completed;

        
    }
}
